# 定制类：
# __str__
'''
class Student(object):
    def __init__(self,name):
        self.name=name
s=Student('Daniel')
print(s)#打印结果为：<__main__.Student object at 0x000002D8767FF4C0>不太好看，使用__str__方法
'''
class Student(object):
    def __init__(self,name):
        self.name=name
    def __str__(self):
        return 'Student object (name：%s)'%self.name
s=Student('Daniel')
print(s)
#第一种直接显示变量调用的不是__str__()，而是__repr__()，两者的区别是__str__()返回用户看到的字符串，而__repr__()返回程序开发者看到的字符串，也就是说，__repr__()是为调试服务的。

#__iter__
#__iter__()方法，该方法返回一个迭代对象，然后，Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值，直到遇到StopIteration错误时退出循环。
class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a，b

    def __iter__(self):
        return self # 实例本身就是迭代对象，故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100000: # 退出循环的条件
            raise StopIteration()
        return self.a # 返回下一个值
for n in Fib():
    print(n)

#__getitem__  要表现得像list那样按照下标取出元素，需要实现__getitem__()方法：
class Fib1(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a
f=Fib1()
print(f[0])
print(f[4])


#list 切片
print(list(range(100))[0:5])

class Fib2(object):
    def __getitem__(self, n):
        if isinstance(n, int): # n是索引
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice): # n是切片
            start = n.start
            stop = n.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L
#切片获取值
f2=Fib2()
print(f2[0:5])
print(f2[1:5])

#__getattr__:动态返回一个属性，可以用来处理调用不存在的属性
class Students(object):
    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99
        if attr=='age':#返回函数也是完全可以
            return lambda:25
s=Students()
print(s.name)
print(s.score)#当调用不存在的属性
print(s.age())#返回函数也是完全可以
print(s.abc)
#注意到任意调用如s.abc都会返回None，这是因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性，我们就要按照约定，抛出AttributeError的错误：
'''class Students(object):
    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99
        if attr=='age':#返回函数也是完全可以
            return lambda:25
        raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)'''
#API调用，详细参考：https://www.liaoxuefeng.com/wiki/1016959663602400/1017590712115904
#__call__：任何类，只需要定义一个__call__()方法，就可以直接对实例进行调用
class Studnet1(object):
    def __init__(self,name):
        self.name=name
    def __call__(self):
        print('My name is %s'%self.name)
s=Studnet1('Daniel')
print(s())# self参数不要传入
#__call__()还可以定义参数
#判断一个对象是否能被调用，能被调用的对象就是一个Callable对象
print(callable(max))
print(None)
print(callable([1,2,3,4]))